﻿#include <unistd.h>

#include <fstream>
#include <regex>
#include <vector>
#include <exception>

#include "misc.hpp"
#include "mallocSharedPtr.hpp"
#include "iniReader.hpp"
#include "ary_str.hpp"

using namespace wheels;

size_t iniRead :: split_line__( mallocSharedPtr<char>& buff , ArrayString& rst )
{
	size_t ret = 0 , len = buff.dataLen();

	std::string str;
	
	for( size_t i = 0; i < len; i ++ ){
		char c = buff[ i ];
		if( c == '\r' ){ continue; }  			// 过滤掉回车符号
		if( c == '\n' ){  						// 过滤掉换行符号
			if( !str.empty() ){ 				// 过滤掉空行
				rst.push_back( str );
				str = "";
				ret ++;
			}
			continue;
		}
 
		if( c == ' ' || c == '\t' ) continue;   // 过滤掉空白字符

		str.push_back( c );
	}
	
	return ret;
}

iniRead :: emErrCode
iniRead :: process_root_item__( const std::string& str )
{
	std::string key , value;
	size_t pos = str.find( "=" );
	if( pos == std::string::npos ) return ERR_FIND_ASSIGN;
	
	key = str.substr( 0 , pos );
	value = str.substr( pos + 1 );

	auto rst = m_root_items__.insert( std::make_pair( key , value ) );
	if( rst.second == false ){
		return ERR_KEY_DUPLICATED;
	}
	return OK;
}

iniRead :: emErrCode
iniRead :: process_item__( const std::string& str )
{
	std::string key , value;
	size_t pos = str.find( "=" );
	if( pos == std::string::npos ) return ERR_FIND_ASSIGN;
	
	key = str.substr( 0 , pos );
	value = str.substr( pos + 1 );

	auto it = m_root_sections__.find( m_curr_section__ );
	if( it != m_root_sections__.end() ){
		auto rst = it->second.insert( std::make_pair( key , value ) );
		if( rst.second == false ){
			return ERR_KEY_DUPLICATED;
		}
	}else{
		return ERR_SECTION_NOT_EXIST;
	}

	return OK;
}

bool iniRead :: is_comment__( const std::string& str )
{
	bool ret = false;

	ret = ( str.front() == ';' );
	
	return ret;
}

bool iniRead :: is_section__( const std::string& str )
{
	bool ret = false;

	if( str.front() == '[' && str.back() == ']' ) ret = true;

	return ret;
}

bool iniRead :: is_item__( const std::string& str )
{
	auto it = str.find( "=" );

	if( it != std::string::npos && str.substr( 0, it ).length() > 0 ) return true;

	return false;
}

iniRead :: emErrCode
iniRead :: parse__( mallocSharedPtr<char>& buff )
{
	emErrCode ret = OK;
	ArrayString a_str;
	size_t count = split_line__( buff , a_str );

	for( size_t i = 0; i < count; i ++ ){
		if( is_comment__( a_str[ i ] ) )continue;    // 跳过注释
		if( is_section__( a_str[ i ] ) ){
			std::string str = a_str[ i ];
			m_curr_section__ = str.substr( 1 , str.length() - 2 );
			auto it = m_root_sections__.find( m_curr_section__ );
			if( it == m_root_sections__.end() ){
				m_root_sections__.insert( std::make_pair( m_curr_section__ , item_t() ) );
			}
		}

		if( is_item__( a_str[ i ] ) ){
			if( m_curr_section__.empty() ){ 		// 在根项目下添加记录
				ret = process_root_item__( a_str[ i ] );			      
			}else{ 									// 在指定小节下添加记录
				ret = process_item__( a_str[ i ] );
			}

			if( ret != OK ) break;
		}
	}

	return ret;
}

iniRead :: iniRead( const std::string& file )
{
	assert( !file.empty() );
	if( access( file.c_str() , F_OK ) != 0 ) throw std::runtime_error( "文件不存在" );
	size_t fsize = getFileSize( file ) , rsize = 0;
	mallocSharedPtr< char >   buff( fsize + 10 );

	std::ifstream ifs( file.c_str() , std::ios::binary | std::ios::in );
	if( ifs.is_open() == false ){
		throw std::runtime_error( "打开文件失败" );
	}

	bool rst = ifs.read( buff.get() , fsize ).bad();
	if( rst ){
		throw std::runtime_error( "读取文件失败" );
	}
	rsize = ifs.tellg();
	buff[ rsize ] = '\0';
	buff.dataLen( rsize );
	
	auto rc = parse__( buff );
	if( rc != OK ){
		MSG_1( "解析INI文件错误 , %d" , TRED, rst );
		throw rst;
	}
}

iniRead :: ~iniRead(){}

bool iniRead :: save( const std::string& file )
{
	bool ret = false;

	if( access( file.c_str() , F_OK ) == 0 ) {
		return false;
	}

	std::stringstream ss;

	// 处理根项目
	for( auto it = m_root_items__.begin(); it != m_root_items__.end(); it ++ ){
		ss << it->first << " = " << it->second << "\n";
	}

	// 处理所有的小节
	for( auto sit = m_root_sections__.begin(); sit != m_root_sections__.end(); sit ++ ){
		ss << "\n[ " << sit->first << " ]\n";

		for( auto it = sit->second.begin(); it != sit->second.end(); it ++ ){
			ss << it->first << " = " << it->second << "\n";
		}
	}

	std::ofstream ofs( file.c_str() , std::ios::out | std::ios::binary );

	if( ofs.is_open() ){
		std::string str = ss.str();
		ofs.write( str.c_str() , str.length() );
		ret = !ofs.bad();
	}
	
	return ret;
}
